using System;
using System.Collections.Generic;
using System.Text;
using System.Xml.Serialization;

namespace A4G.Core
{
	[Serializable]
	public struct Rectangle
	{
		public Rectangle(Rectangle rect)
			: this(rect.Coordinate1, rect.Coordinate2)
		{
		}

		public Rectangle(int x1, int y1, int x2, int y2)
			: this(new Coordinate(x1, y1), new Coordinate(x2, y2))
		{
		}

		public Rectangle(Coordinate coordinate1, Coordinate coordinate2)
		{
			_coordinate1 = new Coordinate(coordinate1);
			_coordinate2 = new Coordinate(coordinate2);
		}

		private Coordinate _coordinate1;
		public Coordinate Coordinate1
		{
			get { return _coordinate1; }
			set { _coordinate1 = value; }
		}

		private Coordinate _coordinate2;
		public Coordinate Coordinate2
		{
			get { return _coordinate2; }
			set { _coordinate2 = value; }
		}

		[XmlIgnore]
		public int X1
		{
			get { return _coordinate1.X; }
			set { _coordinate1.X = value; }
		}

		[XmlIgnore]
		public int X2
		{
			get { return _coordinate2.X; }
			set { _coordinate2.X = value; }
		}

		[XmlIgnore]
		public int Y1
		{
			get { return _coordinate1.Y; }
			set { _coordinate1.Y = value; }
		}

		[XmlIgnore]
		public int Y2
		{
			get { return _coordinate2.Y; }
			set { _coordinate2.Y = value; }
		}

		[XmlIgnore]
		public int DiffX
		{
			get { return Math.Abs(X2 - X1); }
		}

		[XmlIgnore]
		public int DiffY
		{
			get { return Math.Abs(Y2 - Y1); }
		}

		public void Normalize()
		{
			int tmp = X1;
			X1 = Math.Min(X1, X2);
			X2 = Math.Max(tmp, X2);

			tmp = Y1;
			Y1 = Math.Min(Y1, Y2);
			Y2 = Math.Max(tmp, Y2);
		}

		public bool IsNormalized()
		{
			return ((X1 <= X2) && (Y1 <= Y2));
		}

		public override string ToString()
		{
			return string.Format("({0},{1})", Coordinate1, Coordinate2);
		}

		public bool Contains(Coordinate coordinate)
		{
			return Contains(coordinate, true);
		}

		public bool Contains(Coordinate coordinate, bool includeBoundaries)
		{
			Rectangle rect = this;
			rect.Normalize();
			if (!includeBoundaries)
			{
				rect.Increment(-1);
			}

			return ((coordinate.X >= rect.X1) &&
					(coordinate.X <= rect.X2) &&
					(coordinate.Y >= rect.Y1) &&
					(coordinate.Y <= rect.Y2));
		}

		public void Increment(int increment)
		{
			if (X1 < X2)
			{
				X1 += increment;
				X2 -= increment;
			}
			else
			{
				X1 -= increment;
				X2 += increment;
			}

			if (Y1 < Y2)
			{
				Y1 += increment;
				Y2 -= increment;
			}
			else
			{
				Y1 -= increment;
				Y2 += increment;
			}
		}

		public void Limit(Rectangle maxRect)
		{
			maxRect.Normalize();

			if (X1 <= X2)
			{
				if (X1 < maxRect.X1)
				{
					X1 = maxRect.X1;
				}
				if (X2 > maxRect.X2)
				{
					X2 = maxRect.X2;
				}
			}
			else
			{
				if (X1 < maxRect.X2)
				{
					X1 = maxRect.X2;
				}
				if (X2 > maxRect.X1)
				{
					X2 = maxRect.X1;
				}
			}

			if (Y1 <= Y2)
			{
				if (Y1 < maxRect.Y1)
				{
					Y1 = maxRect.Y1;
				}
				if (Y2 > maxRect.Y2)
				{
					Y2 = maxRect.Y2;
				}
			}
			else
			{
				if (Y1 < maxRect.Y2)
				{
					Y1 = maxRect.Y2;
				}
				if (Y2 > maxRect.Y1)
				{
					Y2 = maxRect.Y1;
				}
			}
		}

		public Coordinate GetRelativeCoordinate(Coordinate absoluteCoordinate)
		{
			return new Coordinate(
				absoluteCoordinate.X - Math.Min(X1, X2),
				absoluteCoordinate.Y - Math.Min(Y1, Y2));
		}

		public void Inverse()
		{
			Coordinate tmp = Coordinate1;
			Coordinate1 = Coordinate2;
			Coordinate2 = tmp;
		}
	}
}
